package fr.unice.polytech.chord.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Paint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;

import org.apache.commons.collections15.Transformer;

import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.PluggableGraphMouse;
import edu.uci.ics.jung.visualization.control.RotatingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.ScalingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.TranslatingGraphMousePlugin;
import edu.uci.ics.jung.visualization.decorators.AbstractEdgeShapeTransformer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import fr.unice.polytech.chord.core.ChordNode;
import fr.unice.polytech.chord.core.Entry;
import fr.unice.polytech.chord.core.Finger;
import fr.unice.polytech.chord.core.FingerTable;
import fr.unice.polytech.chord.core.Key;
import fr.unice.polytech.chord.core.Tracker;
import fr.unice.polytech.chord.gui.components.JLimitedSizeTextField;
import fr.unice.polytech.chord.gui.dialogs.AddChordNodeDialog;
import fr.unice.polytech.chord.gui.dialogs.AddEntryDialog;
import fr.unice.polytech.chord.gui.dialogs.RemoveChordNodeDialog;
import fr.unice.polytech.chord.gui.dialogs.RemoveEntryDialog;
import fr.unice.polytech.chord.gui.dialogs.ShowEntriesDialog;

/**
 * ChordVisualizer is the graphical user interface.
 * 
 * @author Anthony Parisy
 * @author Laurent Pellegrino
 * @author Marc Valdener
 * 
 * @version 06/11/2009
 */
@SuppressWarnings("serial")
public class ChordVisualizer extends JFrame {

	private int selectedNode = -1;
	private List<Integer> fingerTableOfSelectedNode = new ArrayList<Integer>();

	private ChordDisplayer chordDisplayer;
	private VisualizationViewer<Integer, String> visualizationViewer;

	private Tracker tracker;

	private JPanel fingerTablePanel;
	private JPanel graphPanel;
	private JPanel informationPanel;
	private JPanel informationSubPanel;
	private JPanel statusBarPanel;

	private JDialog addChordNodeDialog;
	private JDialog removeChordNodeDialog;
	private JDialog addEntryDialog;
	private JDialog removeEntryDialog;
	private JDialog showEntriesDialog;

	/**
	 * Constructor for the GUI
	 */
	public ChordVisualizer(Tracker tracker) {
		super("Chord Visualizer");
		this.switchLookAndFeelToNimbus();

		this.tracker = tracker;
		this.chordDisplayer = new ChordDisplayer(this);
	}

	public void createAndShowGUI() {
		this.graphPanel = this.createGraphPanel();
		this.informationPanel = this.createInformationPanel();
		this.statusBarPanel = this.createStatusBarPanel();
		JPanel container = new JPanel(new BorderLayout());
		container.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

		container.add(this.createToolbar(), BorderLayout.PAGE_START);
		container.add(this.graphPanel, BorderLayout.CENTER);
		container.add(this.informationPanel, BorderLayout.EAST);
		container.add(this.statusBarPanel, BorderLayout.SOUTH);
		super.getContentPane().add(container);

		super.setJMenuBar(this.createMenuBar());
		super.pack();
		super.setResizable(false);
		super.setLocationRelativeTo(null);
		super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		super.setVisible(true);
	}

	private void switchLookAndFeelToNimbus() {
		try {
			for (UIManager.LookAndFeelInfo laf : UIManager
					.getInstalledLookAndFeels()) {
				if ("Nimbus".equals(laf.getName())) {
					UIManager.setLookAndFeel(laf.getClassName());
				}
			}
		} catch (Exception e) {
			System.err
					.println("Please update your java to version >= 1.6 update 10 for "
							+ "Nimbus Look-and-feel. On Mac Os X you need java 1.6 update"
							+ " 17, check update softwares.");
		}
	}

	private JMenuBar createMenuBar() {
		JMenuBar menubar = new JMenuBar();
		JMenu menu;
		JMenuItem item;

		// ----> File
		menu = new JMenu("File");
		menu.setMnemonic(KeyEvent.VK_F);
		menubar.add(menu);

		// Function : Quit
		item = new JMenuItem("Quit", KeyEvent.VK_Q);
		item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
				ActionEvent.CTRL_MASK));
		item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		menu.add(item);

		// ----> Help
		menu = new JMenu("Help");
		menu.setMnemonic(KeyEvent.VK_H);
		menubar.add(menu);

		// Function : Licence
		item = new JMenuItem("Licence");
		item.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				try {
					ChordVisualizer.this.openBrowser(new URL(
							"http://www.opensource.org/licenses/gpl-3.0.html"));
				} catch (MalformedURLException e) {
					e.printStackTrace();
				}
			}
		});
		menu.add(item);

		menu.addSeparator();

		// Function : About
		item = new JMenuItem("About");
		item.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				try {
					ChordVisualizer.this
							.openBrowser(new URL(
									"http://code.google.com/p/chord-proactive/people/list"));
				} catch (MalformedURLException e) {
					e.printStackTrace();
				}
			}
		});
		menu.add(item);

		return menubar;
	}

	public static ImageIcon createImageIcon(String name, String... alt) {
		String legend = name;

		if (alt != null) {
			legend = alt[0];
		}

		return new ImageIcon(ChordVisualizer.class
				.getResource("/icons/" + name), legend);
	}

	private JToolBar createToolbar() {
		JToolBar toolBar = new JToolBar("Manage overlay", JToolBar.HORIZONTAL);
		toolBar.setFloatable(false);
		toolBar.setMargin(new Insets(5, 5, 5, 5));

		ImageIcon iconAddChordNode = ChordVisualizer.createImageIcon(
				"world_add.png", "Join Node");
		ImageIcon iconDeleteChordNode = ChordVisualizer.createImageIcon(
				"world_delete.png", "Leave Node");
		ImageIcon iconAddKey = ChordVisualizer.createImageIcon("key_add.png",
				"Add key");
		ImageIcon iconDeleteKey = ChordVisualizer.createImageIcon(
				"key_delete.png", "Delete key");
		ImageIcon iconRefresh = ChordVisualizer.createImageIcon(
				"arrow_refresh.png", "Refresh");
		ImageIcon iconShowEntries = ChordVisualizer.createImageIcon(
				"table.png", "ShowEntries");

		final JButton btnAddChordNode = new JButton("Add Node",
				iconAddChordNode);
		btnAddChordNode.setAlignmentX(JFrame.CENTER_ALIGNMENT);
		btnAddChordNode.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
		btnAddChordNode.setMargin(new Insets(0, 0, 0, 0));
		btnAddChordNode.setToolTipText("Add Node");
		btnAddChordNode.setMnemonic(KeyEvent.VK_J);
		btnAddChordNode.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				btnAddChordNode.setEnabled(false);
				if (ChordVisualizer.this.addChordNodeDialog == null) {
					ChordVisualizer.this.addChordNodeDialog = new AddChordNodeDialog(
							ChordVisualizer.this, btnAddChordNode);
					((AddChordNodeDialog) ChordVisualizer.this.addChordNodeDialog)
							.createAndShowDialog();
				} else {
					ChordVisualizer.this.addChordNodeDialog.setVisible(true);
				}
			}
		});

		final JButton btnRemoveChordNode = new JButton("Remove Node",
				iconDeleteChordNode);
		btnRemoveChordNode.setAlignmentX(JFrame.CENTER_ALIGNMENT);
		btnRemoveChordNode.setBorder(BorderFactory
				.createEmptyBorder(0, 0, 0, 0));
		btnRemoveChordNode.setMargin(new Insets(0, 0, 0, 0));
		btnRemoveChordNode.setToolTipText("Remove Node");
		btnRemoveChordNode.setMnemonic(KeyEvent.VK_L);
		btnRemoveChordNode.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				btnRemoveChordNode.setEnabled(false);
				if (ChordVisualizer.this.removeChordNodeDialog == null) {
					ChordVisualizer.this.removeChordNodeDialog = new RemoveChordNodeDialog(
							ChordVisualizer.this, btnRemoveChordNode);
					((RemoveChordNodeDialog) ChordVisualizer.this.removeChordNodeDialog)
							.createAndShowDialog();
				} else {
					ChordVisualizer.this.removeChordNodeDialog.setVisible(true);
				}
			}
		});

		final JButton btnAddEntry = new JButton("Add Entry", iconAddKey);
		btnAddEntry.setAlignmentX(JFrame.CENTER_ALIGNMENT);
		btnAddEntry.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
		btnAddEntry.setMargin(new Insets(0, 0, 0, 0));
		btnAddEntry.setToolTipText("Add Entry");
		btnAddEntry.setMnemonic(KeyEvent.VK_P);
		btnAddEntry.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				btnAddEntry.setEnabled(false);
				if (ChordVisualizer.this.addEntryDialog == null) {
					ChordVisualizer.this.addEntryDialog = new AddEntryDialog(
							ChordVisualizer.this, btnAddEntry);
					((AddEntryDialog) ChordVisualizer.this.addEntryDialog)
							.createAndShowDialog();
				} else {
					ChordVisualizer.this.addEntryDialog.setVisible(true);
				}
			}
		});

		final JButton btnRemoveEntry = new JButton("Remove Entry",
				iconDeleteKey);
		btnRemoveEntry.setAlignmentX(JFrame.CENTER_ALIGNMENT);
		btnRemoveEntry.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
		btnRemoveEntry.setToolTipText("Remove Entry");
		btnRemoveEntry.setMnemonic(KeyEvent.VK_D);
		btnRemoveEntry.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				btnRemoveEntry.setEnabled(false);
				if (ChordVisualizer.this.removeEntryDialog == null) {
					ChordVisualizer.this.removeEntryDialog = new RemoveEntryDialog(
							ChordVisualizer.this, btnRemoveEntry);
					((RemoveEntryDialog) ChordVisualizer.this.removeEntryDialog)
							.createAndShowDialog();
				} else {
					ChordVisualizer.this.removeEntryDialog.setVisible(true);
				}
			}
		});

		final JButton btnShowEntries = new JButton("Show Entries",
				iconShowEntries);
		btnShowEntries.setAlignmentX(JFrame.CENTER_ALIGNMENT);
		btnShowEntries.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
		btnShowEntries.setToolTipText("Show Entries");
		btnShowEntries.setMnemonic(KeyEvent.VK_E);
		btnShowEntries.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				btnShowEntries.setEnabled(false);
				if (ChordVisualizer.this.showEntriesDialog == null) {
					ChordVisualizer.this.showEntriesDialog = new ShowEntriesDialog(
							ChordVisualizer.this, btnShowEntries,
							ChordVisualizer.this.tracker);
					((ShowEntriesDialog) ChordVisualizer.this.showEntriesDialog)
							.createAndShowDialog();
				} else {
					ChordVisualizer.this.showEntriesDialog.setVisible(true);
				}
			}
		});

		final JButton btnRefresh = new JButton("Refresh", iconRefresh);
		btnRefresh.setAlignmentX(JFrame.CENTER_ALIGNMENT);
		btnRefresh.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
		btnRefresh.setToolTipText("Refresh");
		btnRefresh.setMnemonic(KeyEvent.VK_R);
		btnRefresh.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				btnRefresh.setEnabled(false);
				ChordVisualizer.this.updateGraph();
				Timer timer = new Timer("refresher");
				timer.schedule(new TimerTask() {
					@Override
					public void run() {
						btnRefresh.setEnabled(true);
						this.cancel();
					}
				}, ChordDisplayer.REFRESH_TIME, 1);
			}
		});

		toolBar.add(btnAddChordNode);
		toolBar.add(btnRemoveChordNode);
		toolBar.addSeparator(new Dimension(20, 0));
		toolBar.add(btnAddEntry);
		toolBar.add(btnRemoveEntry);
		toolBar.addSeparator(new Dimension(20, 0));
		toolBar.add(btnShowEntries);
		toolBar.add(btnRefresh);

		return toolBar;
	}

	public void setSelectedNode(int vertex) {
		while (fingerTableOfSelectedNode.size() > 0) {
			int to = fingerTableOfSelectedNode.remove(0);
			this.chordDisplayer.getGraph().removeEdge(
					"fg:" + selectedNode + "->" + to);
		}

		this.selectedNode = vertex;
		ChordNode node = this.chordDisplayer.getNodes().get(vertex);
		FingerTable fg = node.getFingerTable();

		for (Finger f : fg.getFingers()) {
			int nodeKey = f.getNode().getKey().getValue().intValue();
			this.fingerTableOfSelectedNode.add(nodeKey);
			this.chordDisplayer.getGraph().addEdge(
					"fg:" + vertex + "->" + nodeKey, vertex, nodeKey,
					EdgeType.DIRECTED);
		}
	}

	private JPanel createGraphPanel() {
		graphPanel = new JPanel();
		graphPanel.setLayout(new BorderLayout());
		CircleLayout<Integer, String> circleLayout = new CircleLayout<Integer, String>(
				this.chordDisplayer.getGraph());
		List<Integer> list = new ArrayList<Integer>(this.chordDisplayer
				.getNodes().keySet());
		Collections.sort(list);
		circleLayout.setVertexOrder(list);

		Transformer<Integer, Paint> vertexPaint = new Transformer<Integer, Paint>() {
			public Paint transform(Integer i) {
				if (i == selectedNode) {
					return Color.BLUE;
				} else {
					return Color.RED;
				}
			}
		};
		Transformer<String, Paint> edgePaint = new Transformer<String, Paint>() {
			public Paint transform(String s) {
				if (s.startsWith("fg:")) {
					return Color.BLUE;
				} else {
					return Color.BLACK;
				}
			}
		};
		visualizationViewer = new VisualizationViewer<Integer, String>(
				circleLayout);
		visualizationViewer.getRenderContext().setVertexFillPaintTransformer(
				vertexPaint);
		visualizationViewer.getRenderContext().setVertexLabelTransformer(
				new ToStringLabeller<Integer>());
		visualizationViewer.getRenderContext().setArrowDrawPaintTransformer(
				edgePaint);
		visualizationViewer.getRenderContext().setArrowFillPaintTransformer(
				edgePaint);
		visualizationViewer.getRenderContext().setEdgeDrawPaintTransformer(
				edgePaint);
		AbstractEdgeShapeTransformer<Integer, String> aesf = (AbstractEdgeShapeTransformer<Integer, String>) visualizationViewer
				.getRenderContext().getEdgeShapeTransformer();
		aesf.setControlOffsetIncrement(-20);
		visualizationViewer.repaint();

		PluggableGraphMouse gm = new PluggableGraphMouse();
		gm.add(new TranslatingGraphMousePlugin(MouseEvent.BUTTON3_MASK));
		gm.add(new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0,
				1.1f, 0.9f));
		gm.add(new SelectPlugin(MouseEvent.BUTTON1_MASK));
		gm.add(new RotatingGraphMousePlugin());
		visualizationViewer.setGraphMouse(gm);

		graphPanel.add(visualizationViewer, BorderLayout.CENTER);
		return graphPanel;
	}

	private JPanel createInformationPanel() {
		JPanel informationPanel = new JPanel();
		informationPanel.setPreferredSize(new Dimension(250, 0));
		informationPanel.setLayout(new BoxLayout(informationPanel,
				BoxLayout.Y_AXIS));
		informationPanel
				.setBorder(BorderFactory.createEmptyBorder(0, 25, 0, 0));
		informationPanel.add(this.createLookupPanel());

		return informationPanel;
	}

	private void createSelectedPeerInformationPanel() {
		this.informationSubPanel = this.createSelectedPeerInformationSubPanel();
		this.fingerTablePanel = this.createFingerTableInformationPanel();

		this.informationPanel.add(this.informationSubPanel);
		this.informationPanel.add(this.fingerTablePanel);
	}

	private void updateInformationPanel() {
		if (this.chordDisplayer.getNodes().get(this.selectedNode) != null
				&& this.informationPanel.getComponentCount() == 1) {
			this.createSelectedPeerInformationPanel();
			this.informationPanel.validate();
		}

		if (this.informationPanel.getComponentCount() > 1) {
			this.updateFingerTable();
			this.updateInformation();
		}
		this.updateStatusBar();
	}

	private JPanel createSelectedPeerInformationSubPanel() {
		JPanel selectedPeerInformationSubPanel = new JPanel();
		selectedPeerInformationSubPanel.setLayout(new GridLayout(2, 1, 0, 10));
		selectedPeerInformationSubPanel.setBorder(BorderFactory
				.createTitledBorder("Information on Node"));

		JLabel predecessorText = new JLabel(
				"The predecessor of...",
				ChordVisualizer.createImageIcon("bullet_go.png", "Predecessor"),
				JLabel.LEFT);
		JLabel successorText = new JLabel("The successor of...",
				ChordVisualizer.createImageIcon("bullet_go.png", "Successor"),
				JLabel.LEFT);
		successorText.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));

		selectedPeerInformationSubPanel.add(predecessorText);
		selectedPeerInformationSubPanel.add(successorText);
		selectedPeerInformationSubPanel.setMaximumSize(new Dimension(250,
				(int) selectedPeerInformationSubPanel.getPreferredSize()
						.getHeight()));

		return selectedPeerInformationSubPanel;
	}

	private JPanel createFingerTableInformationPanel() {
		JPanel fingerTablePanel = new JPanel(new BorderLayout());
		fingerTablePanel.setBorder(BorderFactory
				.createTitledBorder("Finger Table"));

		String data[][] = new String[Key.NB_BITS][2];
		String columnNames[] = { "Node", "Finger" };
		DefaultTableModel model = new DefaultTableModel(data, columnNames) {
			@Override
			public boolean isCellEditable(int row, int column) {
				return false;
			}
		};
		JTable fingerTableTable = new JTable(model);
		fingerTableTable.setDragEnabled(false);
		fingerTableTable.setRowHeight(20);
		fingerTablePanel.add(fingerTableTable.getTableHeader(),
				BorderLayout.NORTH);
		fingerTablePanel.add(fingerTableTable, BorderLayout.CENTER);
		fingerTablePanel.setMaximumSize(new Dimension(250,
				(int) fingerTablePanel.getPreferredSize().getHeight()));

		return fingerTablePanel;
	}

	private void updateFingerTable() {
		try {
			JTable table = ((JTable) this.fingerTablePanel.getComponent(1));

			DefaultTableModel model = (DefaultTableModel) table.getModel();

			int power = 1;
			int i = 0;
			for (Finger finger : this.chordDisplayer.getNodes().get(
					this.selectedNode).getFingerTable().getFingers()) {
				model.setValueAt("N" + this.selectedNode + " + " + power, i, 0);
				model.setValueAt("N" + finger.getNode().getKey(), i, 1);
				i++;
				power *= 2;
			}

			this.fingerTablePanel.setBorder(BorderFactory
					.createTitledBorder("Finger Table of Node "
							+ this.selectedNode));
		} catch (Exception e) {
			// wait for stabilization...
		}
	}

	private void updateInformation() {
		ChordNode selectedChordNode = this.chordDisplayer.getNodes().get(
				this.selectedNode);
		((JLabel) this.informationSubPanel.getComponent(0))
				.setText("The predecessor of " + this.selectedNode + " is "
						+ selectedChordNode.getPredecessor().getKey());
		((JLabel) this.informationSubPanel.getComponent(1))
				.setText("The successor of " + this.selectedNode + " is "
						+ selectedChordNode.getSuccessor().getKey());

		this.informationSubPanel
				.setBorder(BorderFactory
						.createTitledBorder("Information on Node "
								+ this.selectedNode));
	}

	private void updateStatusBar() {
		int nbNodes = this.chordDisplayer.getNodes().size();
		String nodes = nbNodes > 1 ? "nodes" : "node";
		((JLabel) this.statusBarPanel.getComponent(0)).setText(nbNodes + " "
				+ nodes + " on network - Key size is " + Key.NB_BITS + " bits");
	}

	private JPanel createLookupPanel() {
		JPanel lookupPanel = new JPanel(new FlowLayout());
		lookupPanel
				.setBorder(BorderFactory.createTitledBorder("Lookup by Key"));

		JLabel searchLabel = new JLabel(ChordVisualizer.createImageIcon(
				"zoom.png", "Successor"), JLabel.LEFT);
		final JTextField searchTextField = new JLimitedSizeTextField(6,
				Key.NB_BITS);
		final JButton searchButton = new JButton("Search");
		searchTextField.setToolTipText("Key");
		searchTextField.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					searchButton.doClick();
				}
			}
		});
		searchButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				if (ChordVisualizer.this.chordDisplayer.getNodes().size() > 0) {
					try {
						Key key = new Key(Integer.parseInt(searchTextField
								.getText()));

						ChordNode node = ChordVisualizer.this.tracker
								.getRandomPeer().findSuccessor(key);

						boolean nodeExists = key.equals(node.getKey());
						Entry<String> result = node.getEntry(key);

						if (nodeExists && node.getNbEntries() == 0) {
							JOptionPane
									.showMessageDialog(
											ChordVisualizer.this,
											"A node with key "
													+ key
													+ " exists on the overlay\nhowever it hasn't entry associated\nwith this key",
											"Lookup",
											JOptionPane.INFORMATION_MESSAGE);
						} else if (nodeExists && node.getNbEntries() > 0) {
							new ShowEntriesDialog(ChordVisualizer.this, node,
									"Show Entries of Node " + node.getKey())
									.createAndShowDialog();
						} else if (!nodeExists && result != null) {
							JOptionPane.showMessageDialog(ChordVisualizer.this,
									"An entry has been found for key " + key
											+ "\nwith value '" + result
											+ "' on node " + node.getKey(),
									"Lookup", JOptionPane.INFORMATION_MESSAGE);
						} else {
							JOptionPane.showMessageDialog(ChordVisualizer.this,
									"No node and no entry found on the overlay\nwith key "
											+ key, "Lookup",
									JOptionPane.INFORMATION_MESSAGE);
						}
					} catch (NumberFormatException e) {
						JOptionPane.showMessageDialog(ChordVisualizer.this,
								"Key must be a positive number", "Lookup",
								JOptionPane.INFORMATION_MESSAGE);
					}
				} else {
					JOptionPane.showMessageDialog(ChordVisualizer.this,
							"There is no node on the overlay", "Lookup",
							JOptionPane.INFORMATION_MESSAGE);
				}
				searchTextField.setText("");
				searchTextField.requestFocus();
			}
		});
		lookupPanel.add(searchLabel);
		lookupPanel.add(searchTextField);
		lookupPanel.add(searchButton);
		lookupPanel.setMaximumSize(new Dimension(250, (int) lookupPanel
				.getPreferredSize().getHeight()));

		return lookupPanel;
	}

	private JPanel createStatusBarPanel() {
		JPanel statusBarPanel = new JPanel();
		statusBarPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		statusBarPanel.setLayout(new BorderLayout());
		JLabel status = new JLabel("0 nodes on network");
		statusBarPanel.add(status);

		return statusBarPanel;
	}

	public void updateGraph() {
		try {
			if (this.selectedNode >= 0) {
				this.setSelectedNode(this.selectedNode);
			}
			CircleLayout<Integer, String> circleLayout = new CircleLayout<Integer, String>(
					this.chordDisplayer.getGraph());
			List<Integer> list = new ArrayList<Integer>(this.chordDisplayer
					.getNodes().keySet());
			Collections.sort(list);
			circleLayout.setVertexOrder(list);
			visualizationViewer.getModel().setGraphLayout(circleLayout);
			visualizationViewer.repaint();
			this.updateInformationPanel();
		} catch (Exception e) {
			// do nothing, wait stabilization...
		}
	}

	public Tracker getTracker() {
		return this.tracker;
	}

	private class SelectPlugin extends AbstractGraphMousePlugin implements
			MouseListener {

		public SelectPlugin(int modifiers) {
			super(modifiers);
		}

		@SuppressWarnings("unchecked")
		@Override
		public void mouseClicked(MouseEvent e) {
			if (checkModifiers(e)) {
				final VisualizationViewer<Integer, String> visulatizationViewer = (VisualizationViewer<Integer, String>) e
						.getSource();
				final Point2D p = e.getPoint();
				GraphElementAccessor<Integer, String> pickSupport = visulatizationViewer
						.getPickSupport();
				if (pickSupport != null) {
					final Integer vertex = pickSupport.getVertex(
							visulatizationViewer.getModel().getGraphLayout(), p
									.getX(), p.getY());
					if (vertex != null) { // get ready to make an edge
						ChordVisualizer.this.setSelectedNode(vertex);
						visulatizationViewer.repaint();
						ChordVisualizer.this.updateInformationPanel();
					}
				}
			}
		}

		@Override
		public void mouseEntered(MouseEvent e) {
		}

		@Override
		public void mouseExited(MouseEvent e) {
		}

		@Override
		public void mousePressed(MouseEvent e) {
		}

		@Override
		public void mouseReleased(MouseEvent e) {
		}
	}

	private void openBrowser(URL url) {
		boolean onError = false;

		if (Desktop.isDesktopSupported()) {
			Desktop desktop = Desktop.getDesktop();
			if (desktop.isSupported(Desktop.Action.BROWSE)) {
				try {
					desktop.browse(url.toURI());
				} catch (IOException e) {
					onError = true;
				} catch (URISyntaxException e) {
					onError = true;
				}
			} else {
				onError = true;
			}
		} else {
			onError = true;
		}

		if (onError) {
			JOptionPane
					.showMessageDialog(
							null,
							"Error while launching web browser.\n"
									+ "You can read terms of licence to the following URL :\n"
									+ "http://www.opensource.org/licenses/gpl-3.0.html",
							"Browser not found", JOptionPane.ERROR_MESSAGE);
		}
	}

}
